package com.zty.tool;

import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @Author ZTY
 * @Description 动态配置读取
 * @Date 21:50 2024-01-11
 **/

public class PropertiesUtils {

    //  属性文件map
    private Map<String, Properties> propertiesMap = new HashMap<>();

    //  属性文件最后修改时间map
    private Map<String, Long> modifyTimeMap = new HashMap<>();

    //  首先得需要文件，因为动态读取的是属性文件
    //  属性文件存储路径
    private String configPath = "";

    public void setConfigPath(String path) {
        this.configPath = path;
    }

    //  使用单例模式，一次就初始化完成
    //  使用单例模式，外部使用的时候可以直接getInstance拿到这个类
    private PropertiesUtils() {
    }

    private static class SingleHolder {
        private static PropertiesUtils instance = new PropertiesUtils();
    }

    public static PropertiesUtils getInstance() {
        return SingleHolder.instance;
    }


    //  通过属性的key，便可得到其value
    public String getPropertyValue(String propertyFileName, String key) throws Exception {
        //  文件名去后缀
        String fileName = convertFileName(propertyFileName);

        if (propertiesMap.get(fileName) == null) {
            //  将文件名加载进map中
            loadProperties(fileName);
        } else {
            //  检查属性文件是否有被修改
            //  通过属性文件的修改时间判断其是否为最新的，如果修改的时间和上次读取的时间不一致，证明文件被修改过，要加载最新的配置
            checkPropertiesModified(fileName);
        }

        return propertiesMap.get(fileName).getProperty(key);
    }

    /**
     * 通过属性文件的修改时间判断其是否为最新的，如果修改的时间和上次读取的时间不一致，证明文件被修改过，要加载最新的配置
     *
     * @param fileName
     */
    private void checkPropertiesModified(String fileName) throws Exception {
        //  根据文件名拿到file文件
        File file = getPropertyFile(fileName);
        long newTime = file.lastModified();
        Long lastTime = modifyTimeMap.get(fileName);

        if (newTime > lastTime) {
            loadProperties(fileName);
        }
    }

    /**
     * 将文件加载进map中，方便读取
     *
     * @param fileName
     */
    private void loadProperties(String fileName) throws Exception {
        //  根据文件名拿到file文件
        File file = getPropertyFile(fileName);
        long lastTime = file.lastModified();

        if (propertiesMap.get(fileName) != null) {
            propertiesMap.remove(fileName);
        }

        //  重新读取配置
        Properties properties = new Properties();
        properties.load(Files.newInputStream(file.toPath()));
        propertiesMap.put(fileName, properties);
        modifyTimeMap.put(fileName, lastTime);
    }

    /**
     * 根据文件名拿到file文件
     *
     * @param fileName
     * @return
     */
    private File getPropertyFile(String fileName) throws Exception {
        File propertiesFile = null;

        //  如果有指定路径，则读取指定路径
        if (this.configPath != null && !StringUtils.isBlank(configPath)) {
            return new File(this.configPath + File.separator + fileName + ".properties");
        }

        //  如果没有指定路径，则从用户地址获取
        String dir = System.getProperty("user.dir") + File.separator + fileName + ".properties";
        propertiesFile = new File(dir);

        //  如果还是不存在，则进入最终阶段，读取项目resource中的classpath
        if (!propertiesFile.exists()) {
            URL url = Properties.class.getResource("/" + fileName + ".properties");
            if (url != null) {
                propertiesFile = new File(url.toURI());
            }
        }
        return propertiesFile;
    }

    /**
     * 文件名去后缀
     *
     * @param propertyFileName
     * @return
     */
    private String convertFileName(String propertyFileName) {

        //  判断是否以.properties结尾
        if (!propertyFileName.endsWith(".properties")) {
            return propertyFileName;
        }

        //  若以.properties结尾，则将后缀去掉
        int index = propertyFileName.lastIndexOf(".");
        return propertyFileName.substring(0, index);
    }

}
